#lang racket

(require "primitives.rkt")
(require "tunn_common.rkt")
(require "arcfour.rkt")

;Constructs an obfuscation tunnel.
(define (make-obfs-tunnel
         lport
         rhost
         rport)
  
  (define listener (tcp-listen lport 16 #t "127.0.0.1"))
  ;Pass pointer to this value around
  (define accbytes (box 0))
  
  ;Outermost thread: the listen-forward loop
  (thread
   (λ()
     (let loop ()
       (define client (tcp-accept-client listener))
       ;Forkout thread: begin forwarding
       (thread
        (λ()
          ;Connect with remote server
          (displayln "Connecting with remote server..." (current-error-port))
          (define remote (tcp-connect-client rhost rport))
          
          ;Obtain bruteforce challenge
          (displayln "Obtaining bruteforce challenge..." (current-error-port))
          (define challenge (make-bytes 256))
          (read-bytes! challenge (tcpclient-input remote))
          
          ;Brute force the key
          (displayln "Bruteforcing key..." (current-error-port))
          (define key (rc4-findkey (list->bytes (build-list 256 (λ x 0)))
                                   challenge))
          (displayln "Key found..." (current-error-port))
          
          ;RC4 copy port
          (define (rc4-copy-port p1 p2 bytesbox chugger)
            (cond
              [(port-closed? p1) (void)]
              [(port-closed? p2) (void)]
              [else
               (define buffer (make-bytes 16384))
               (define c (read-bytes-avail! buffer p1))
               (cond
                 [(eof-object? c) (void)]
                 [else 
                  (set! buffer (chugger buffer c))
                  (write-bytes buffer p2 0 c)
                  (flush-output p2)
                  ;Updates the byte count box
                  (set-box! bytesbox
                            (+ c (unbox bytesbox)))
                  (rc4-copy-port p1 p2 bytesbox chugger)
                  ])]))
          
          ;Upstream forks away
          (thread 
           (λ()
             (rc4-copy-port (tcpclient-input client)
                           (tcpclient-output remote) accbytes 
                           (arraychugger (make-rc4 key)))
             (close-output-port (tcpclient-output remote))
             (close-input-port (tcpclient-input client))
             ))
          ;Downstream stays in same thread
          (rc4-copy-port (tcpclient-input remote)
                        (tcpclient-output client) accbytes
                        (arraychugger (make-rc4 key))
                        )
          (close-input-port (tcpclient-input remote))
          (close-output-port (tcpclient-output client))))
       (loop))
     ))
  
  (tunnel
   'obfs
   lport
   rhost
   rport
   listener
   accbytes
   (box 0)
   (box (current-inexact-milliseconds))
   (λ() (tcp-close listener))))
(provide (all-defined-out))